
package cplex;

import ilog.concert.*;
import ilog.cplex.IloCplex;


public class BinPackingCol {
    
    static class IloNumVarArray{ // "Array", das erweitert werden kann;
        int number = 0;          // wird fuer die neu erzeugten Variablen benoetigt
        IloNumVar[] array = new IloNumVar[32];
        
        void add(IloNumVar newVar){
            if (number >= array.length){
                IloNumVar[] tempArray = new IloNumVar[2 * array.length];
                System.arraycopy(array, 0, tempArray, 0, number-1);
                
                array = tempArray;
                
            }
            array[number] = newVar;
            number++;
        }
        IloNumVar[] getArray(){
            return array;
        }
        IloNumVar getVar(int i){
            return array[i];
        }
        int size(){
            return number;
        }
    }
    
    public static void main(String[] args){
        try{
            IloCplex cplex = new IloCplex(); // Loeser fuer das Hauptproblem (Bin Packing)
           
            IloNumVarArray var = new IloNumVarArray();// Variablen des Hauptproblems (Bin Packing)
            
            IloRange[]  rng  = new IloRange[3];  // Constraints/Beschraenkungen des Hauptproblems
            IloObjective cost = cplex.addMinimize();
            
            populateByColumnAtBeginning(cplex, cost, var, rng);
            
            IloCplex configurationGen = new IloCplex(); // Loeser fuer das Pricing Problem (Column Generation)
            
            double[] itemSizes = {1.0/2.0, 1.0/3.0, 1.0/4.0};  // Itemgroessen: wichtig 1.0/... zu schreiben, bei 1/... ist das Ergebnis immer 0
            double[] pis        = {1.0, 0.0, 0.0};             //Platzhalter, um Zielfunktion des configurationGen zu definieren (profit)
            IloIntVar[] varGen = configurationGen.intVarArray(3, 0, Integer.MAX_VALUE); // Variablen des Pricing Problems
            IloObjective profit = configurationGen.addMaximize(configurationGen.scalProd(pis, varGen));
            
            initializeColumnGen(configurationGen, varGen, itemSizes);
            
            boolean abbruch = false;
            int zahlDurchlauf = 0;
            
            while(!abbruch){
            
                zahlDurchlauf++;
                
                if(cplex.solve()) {
                    cplex.output().println("+--------------------------+");
                    cplex.output().println("Successfully solved.");
                    
                    //double[] x  = cplex.getValues(var.getArray()); //Funktioniert leider nicht, deswegen werden weiter unten 
                    // einzelne Variablen abgefragt mit getValue()
                    double[] pi = cplex.getDuals(rng);

                    for (int j = 0; j < var.size(); j++){
                        cplex.output().println("Value of Variable " + j + " = " + cplex.getValue(var.getVar(j)));
                    }

                    for (int j = 0; j < 3; j++){
                        cplex.output().println("Value of Dual Variable " + j + " = " + pi[j]);
                    }
                    
                    cplex.output().println("Number of bins needed: " + cplex.getObjValue());
                    
                    cplex.output().println("+--------------------------+");
                    
                    configurationGen.setLinearCoefs(profit, pi, varGen);
                    
                    if(configurationGen.solve()){
                          if(configurationGen.getObjValue() > 1){
                              double[] z = configurationGen.getValues(varGen);
                              addColumn(cplex, cost, var, rng, z);
                        }else{
                              abbruch = true;
                              cplex.output().println("+---------------------------------------------------+");
                              cplex.output().println("No new columns found in iteration " + zahlDurchlauf + ".");
                              cplex.output().println(cplex.getObjValue() + " bins are needed.");
                              cplex.output().println("The solution is: " + cplex.getStatus() );
                              cplex.output().println("+---------------------------------------------------+");
                        }
                    }
                   
            }
            
            
        }
        }
        catch (IloException e) {
         System.err.println("Concert exception '" + e + "' caught");
    }
        
    }

        static void populateByColumnAtBeginning (IloMPModeler model,
                                IloObjective cost, IloNumVarArray var,
                                IloRange[] rng) throws IloException {
            
            rng[0] = model.addRange(4,Double.MAX_VALUE, "c1");
            rng[1] = model.addRange(2,Double.MAX_VALUE, "c2");
            rng[2] = model.addRange(6,Double.MAX_VALUE, "c3");
            
            //IloColumn col1 = model.column(cost,   1.0).and(
//                             model.column(rng[0], 1.0).and(
//                             model.column(rng[1], 0.0).and(
//                             model.column(rng[2], 0.0))));
//            IloColumn col2 = model.column(cost,   1.0).and(
//                             model.column(rng[0], 0.0).and(
//                             model.column(rng[1], 2.0).and(
//                             model.column(rng[2], 1.0))));
//            IloColumn col3 = model.column(cost,   1.0).and(
//                             model.column(rng[0], 0.0).and(
//                             model.column(rng[1], 0.0).and(
//                             model.column(rng[2], 4.0))));
              IloColumn col1 = model.column(cost,   12).and(
                               model.column(rng[0], 2.0).and(
                               model.column(rng[1], 2.0).and(
                               model.column(rng[2], 2.0))));
             
              
            var.add(model.numVar(col1, 0.0, Double.MAX_VALUE, "x1"));
//            var.add(model.numVar(col2, 0.0, Double.MAX_VALUE, "x2"));
//            var.add(model.numVar(col3, 0.0, Double.MAX_VALUE, "x3"));
        }
        
        static void initializeColumnGen (IloMPModeler model,
                IloNumVar[] varGen, double[] itemSizes) throws IloException {
            model.addRange(0, model.scalProd(varGen, itemSizes), 1);
        }
        
        static void addColumn (IloMPModeler model, IloObjective cost, IloNumVarArray var, IloRange[] rng, double[] z) 
                throws IloException {
            IloColumn col = model.column(cost, 1.0);
            for (int j = 0; j < z.length; j++) {
                    col = col.and(model.column(rng[j], z[j]));
                }
           
            var.add(model.numVar(col, 0.0, Double.MAX_VALUE));
        }

}